package com.tyk.bookstore.back.storage.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tyk.bookstore.back.common.core.model.type.BinVo;
import com.tyk.bookstore.back.common.core.model.type.SuggestionQuery;
import com.tyk.bookstore.back.common.core.util.AssertUtil;
import com.tyk.bookstore.back.common.core.util.IdGenerator;
import com.tyk.bookstore.back.common.core.util.ObjUtil;
import com.tyk.bookstore.back.platform.service.AreaApiService;
import com.tyk.bookstore.back.storage.manager.excel.model.WarehouseExcelVo;
import com.tyk.bookstore.back.storage.model.entity.StockDo;
import com.tyk.bookstore.back.storage.model.entity.WarehouseDo;
import com.tyk.bookstore.back.storage.model.query.WarehouseAddQuery;
import com.tyk.bookstore.back.storage.model.query.WarehousePageQuery;
import com.tyk.bookstore.back.storage.model.query.WarehouseUpdateQuery;
import com.tyk.bookstore.back.storage.model.result.WarehouseResultEnum;
import com.tyk.bookstore.back.storage.model.vo.WarehousePageVo;
import com.tyk.bookstore.back.storage.model.vo.WarehouseVo;
import com.tyk.bookstore.back.storage.repository.StockRepository;
import com.tyk.bookstore.back.storage.repository.WarehouseRepository;
import com.tyk.bookstore.back.storage.service.WarehouseService;
import com.tyk.bookstore.back.storage.util.MapStructStorage;
import com.tyk.bookstore.back.system.service.UserDetailApiService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

import java.math.BigInteger;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author tyk
 * @description 仓库管理
 */
@Service
@RequiredArgsConstructor
public class WarehouseServiceImp implements WarehouseService {
    private final WarehouseRepository warehouseRepository;
    private final StockRepository stockRepository;
    private final UserDetailApiService userDetailApiService;
    private final TransactionTemplate transactionTemplate;
    private final MapStructStorage mapStructStorage;
    private final AreaApiService areaApiService;


    @Override
    public WarehouseVo getById(Long id) {

        // 仓库存在
        AssertUtil.isTrue(
                warehouseRepository.exist1Eq(WarehouseDo::getId, id, WarehouseDo.class),
                WarehouseResultEnum.WAREHOUSE_NOT_FOUND
        );

        // 转换
        WarehouseDo wd = warehouseRepository.selectById(id);
        WarehouseVo wv = mapStructStorage.toWarehouseVo(wd);

        // 区域名称、负责人名称
        wv.setAreaName(
                areaApiService.getFullNameByCode(wd.getAreaCode())
        );
        wv.setUserName(
                userDetailApiService.getNameById(wd.getUserId())
        );

        return wv;
    }

    @Override
    public WarehousePageVo getPage(WarehousePageQuery query) {

        // 插叙
        Page<WarehouseDo> page = new Page<>(query.getPage(), query.getLimit());
        warehouseRepository.selectPage(page, buildWarehouseQueryWrapper(query));

        // 特判
        if (page.getTotal() == 0L) return WarehousePageVo.instance(WarehousePageVo.class);

        // 转换
        WarehousePageVo wpv = mapStructStorage.toWarehousePageVo(page);


        // 用户名
        Map<Long, String> unMap = userDetailApiService
                .getNameVoListByIdSet(
                        wpv.getRecords().stream().map(WarehousePageVo.Warehouse::getUserId).collect(Collectors.toSet())
                )
                .stream().collect(Collectors.toMap(BinVo::getId, BinVo::getValue));

        // 区域名
        Map<Long, String> anMap = areaApiService
                .getFullNameListByCodeSet(
                        wpv.getRecords().stream().map(WarehousePageVo.Warehouse::getAreaCode).collect(Collectors.toSet())
                ).stream().collect(Collectors.toMap(BinVo::getId, BinVo::getValue));

        // 补充用户名
        wpv.getRecords().forEach(it -> {
            it.setUserName(unMap.get(it.getUserId()))
                    .setAreaFullName(anMap.get(it.getAreaCode()));
        });

        return wpv;
    }

    @Override
    public void add(WarehouseAddQuery query) {

        // 校验区域Code和用户ID
        areaApiService.assertLastAreaByCode(query.getAreaCode());
        userDetailApiService.assertExistById(query.getUserId());

        // 不存在位置相同且名称相同的仓库
        AssertUtil.isFalse(
                warehouseRepository.exist2Eq(
                        WarehouseDo::getAreaCode, query.getAreaCode(),
                        WarehouseDo::getName, query.getName(),
                        WarehouseDo.class
                ),
                WarehouseResultEnum.WAREHOUSE_ALREADY_EXISTS
        );

        // 插入
        WarehouseDo warehouse = mapStructStorage.toWarehouseDo(query);
        warehouse.setId(IdGenerator.id());
        warehouseRepository.insert(warehouse);

    }

    @Override
    public void updateById(WarehouseUpdateQuery query) {

        // 校验区域Code和用户ID
        areaApiService.assertLastAreaByCode(query.getAreaCode());
        userDetailApiService.assertExistById(query.getUserId());

        // ID存在
        AssertUtil.isTrue(
                warehouseRepository.exist1Eq(
                        WarehouseDo::getId, query.getId(),
                        WarehouseDo.class
                ),
                WarehouseResultEnum.WAREHOUSE_NOT_FOUND
        );
        // 不存在位置相同且名称相同的仓库
        AssertUtil.isFalse(
                warehouseRepository.exist2Eq1Ne(
                        WarehouseDo::getAreaCode, query.getAreaCode(),
                        WarehouseDo::getName, query.getName(),
                        WarehouseDo::getId, query.getId(),
                        WarehouseDo.class
                ),
                WarehouseResultEnum.WAREHOUSE_ALREADY_EXISTS
        );

        // 更新
        WarehouseDo warehouse = mapStructStorage.toWarehouseDo(query);
        warehouseRepository.updateById(warehouse);
    }

    @Override
    public void deleteById(Long id) {
        transactionTemplate.executeWithoutResult(status -> {

            // ID存在
            AssertUtil.isTrue(
                    warehouseRepository.exist1Eq(
                            WarehouseDo::getId, id,
                            WarehouseDo.class
                    ),
                    WarehouseResultEnum.WAREHOUSE_NOT_FOUND
            );

            // 当前仓库下的所有库存均为0
            AssertUtil.isFalse(
                    stockRepository.exist1Eq1Ne(
                            StockDo::getWarehouseId, id,
                            StockDo::getNum, 0L,
                            StockDo.class
                    ),
                    WarehouseResultEnum.WAREHOUSE_STILL_HAS_STOCK
            );

            // 删除
            stockRepository.delete1Eq(StockDo::getWarehouseId, id, StockDo.class);
            warehouseRepository.deleteById(id);

        });
    }

    @Override
    public void deleteBatchByIdSet(Set<Long> idSet) {
        transactionTemplate.executeWithoutResult(status -> {

            // 仓库存在
            AssertUtil.equal(
                    warehouseRepository.count1In(WarehouseDo::getId, idSet, WarehouseDo.class),
                    (long) idSet.size(),
                    WarehouseResultEnum.WAREHOUSE_NOT_FOUND
            );

            // 仓库库存为0
            AssertUtil.isFalse(
                    stockRepository.exist1In1Ne(
                            StockDo::getWarehouseId, idSet,
                            StockDo::getNum, 0L,
                            StockDo.class
                    ),
                    WarehouseResultEnum.WAREHOUSE_STILL_HAS_STOCK
            );

            // 删除
            stockRepository.delete1In(
                    StockDo::getWarehouseId, idSet, StockDo.class
            );
            warehouseRepository.deleteBatchIds(idSet);
        });
    }

    @Override
    public List<BinVo> getSuggestionList(SuggestionQuery query) {
        return warehouseRepository
                .<BigInteger, String>selectBinList1Like(
                        WarehouseDo::getId, WarehouseDo::getName,
                        WarehouseDo::getName, query.getKeyword(),
                        WarehouseDo.class
                )
                .stream().map(it -> new BinVo(it.getV1().longValue(), it.getV2())).toList();
    }

    @Override
    public List<WarehouseExcelVo> getWarehouseExcelVoList(WarehousePageQuery query) {
        LambdaQueryWrapper<WarehouseDo> w = buildWarehouseQueryWrapper(query);
        List<WarehouseDo> doList = warehouseRepository.selectList(w);
        return mapStructStorage.toWarehouseExcelVo(doList);
    }

    @Override
    public void saveWarehouseExcelVoList(List<WarehouseExcelVo> warehouseList) {
        List<WarehouseDo> doList = mapStructStorage.toWarehouseDoList(warehouseList);
        doList.forEach(it -> it.setId(IdGenerator.id()));
        warehouseRepository.insertBatch(doList);
    }

    /**
     * 构建仓库分页查询条件
     */
    private LambdaQueryWrapper<WarehouseDo> buildWarehouseQueryWrapper(WarehousePageQuery query) {

        LambdaQueryWrapper<WarehouseDo> w = Wrappers.lambdaQuery(WarehouseDo.class);

        w.eq(query.getId() != null, WarehouseDo::getId, query.getId());

        w.eq(query.getUserId() != null, WarehouseDo::getUserId, query.getUserId());

        if (query.getAreaCode() != null) {
            List<Long> leafIdList = areaApiService.getLeafCodeListByCode(query.getAreaCode());
            w.in(WarehouseDo::getAreaCode, leafIdList);
        }

        w.ge(query.getStartTime() != null, WarehouseDo::getCreateTime, query.getStartTime());
        w.le(query.getEndTime() != null, WarehouseDo::getCreateTime, query.getEndTime());

        w.like(ObjUtil.notBlank(query.getNameLike()), WarehouseDo::getName, query.getNameLike());

        w.eq(ObjUtil.notBlank(query.getSenderPhone()), WarehouseDo::getSenderPhone, query.getSenderPhone());

        if (ObjUtil.notBlank(query.getSenderName())) {
            List<Long> userIdList = userDetailApiService.getIdListByNameLike(query.getSenderName());
            w.in(WarehouseDo::getUserId, userIdList);
        }

        w.orderByDesc(WarehouseDo::getCreateTime);

        return w;
    }
}
